﻿using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using LukasKopenec.LocalizationSupport;
using Notoric.Model.Packaging;
using Notoric.ViewModel.Internal;

namespace Notoric.ViewModel.Elements
{
    /// <summary>
    /// Adapts a <see cref="ResourcePart"/>.
    /// </summary>
    public class Resource : IProjectExplorerItem
    {
        #region Constructors

        /// <summary>
        /// Creates a new <see cref="Resource"/> instance.
        /// </summary>
        /// <param name="adaptedPart">
        /// <para>
        /// Type: <see cref="ResourcePart"/>
        /// </para>
        /// <para>
        /// The resource package part adapted by this resource.
        /// </para>
        /// </param>
        /// <param name="owner">
        /// <para>
        /// Type: <see cref="Project"/>
        /// </para>
        /// <para>
        /// The project that directly owns this resource.
        /// </para>
        /// </param>
        /// <param name="name">
        /// <para>
        /// Type: <see cref="System.String"/>
        /// </para>
        /// <para>
        /// The name of the resource.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Any of the arguments is <see langword="null"/>.
        /// </exception>
        public Resource(ResourcePart adaptedPart, Project owner, string name)
        {
            if (adaptedPart == null)
                throw new ArgumentNullException("adaptedPart");
            if (owner == null)
                throw new ArgumentNullException("owner");
            if (name == null)
                throw new ArgumentNullException("name");

            this.adaptedPart = adaptedPart;
            this.extension = System.IO.Path.GetExtension(name);
            this.name = name;
            this.owner = owner;
            var pathElement = ((Model.Elements.ProjectElement)owner.AdaptedElement).Resources.First(pe => pe.URI == adaptedPart.Uri);
            pathElement.PropertyChanged += new PropertyChangedEventHandler(pathElement_PropertyChanged);

            projectExplorerHelper = new ProjectExplorerItemHelper(owner, this);
            projectExplorerHelper.PropertyChanged += new PropertyChangedEventHandler(projectExplorerHelper_PropertyChanged);

            if (extension != null && extension != "")
            {
                var icon = Icons.IconFromExtension(extension, SystemIconSize.Small);
                if (icon != null)
                {
                    Bitmap bitmap = ((Icon)icon).ToBitmap();
                    IntPtr hBitmap = bitmap.GetHbitmap();

                    ImageSource wpfBitmap =
                        Imaging.CreateBitmapSourceFromHBitmap(
                             hBitmap, IntPtr.Zero, Int32Rect.Empty,
                             BitmapSizeOptions.FromEmptyOptions());

                    projectExplorerHelper.SetIcon(wpfBitmap);
                }
            }

            CreateContextMenu();
        }

        #endregion

        #region IProjectExplorerItem Members

        /// <inheritdoc/>
        public ContextMenu ContextMenu
        {
            get { return projectExplorerHelper.ContextMenu; }
        }

        /// <inheritdoc/>
        public string DisplayName
        {
            get
            {
                return name;
            }
        }

        /// <inheritdoc/>
        public ImageSource Icon
        {
            get { return projectExplorerHelper.Icon; }
        }

        /// <inheritdoc/>
        public bool IsExpanded
        {
            get
            {
                return projectExplorerHelper.IsExpanded;
            }
            set
            {
                projectExplorerHelper.IsExpanded = value;
            }
        }

        /// <inheritdoc/>
        public bool IsInEditMode
        {
            get { return projectExplorerHelper.IsInEditMode; }
            set { projectExplorerHelper.IsInEditMode = value; }
        }

        /// <inheritdoc/>
        public bool IsSelected
        {
            get
            {
                return projectExplorerHelper.IsSelected;
            }
            set
            {
                projectExplorerHelper.IsSelected = value;
            }
        }

        /// <inheritdoc/>
        public string Name
        {
            get { return name; }
            set
            {
                var project = ProjectExplorerItemHelper.GetProject(this);
                var cmd = new RenameResourceCommand(project.UndoStack, project.RedoStack)
                {
                    RenamedPart = this,
                    VirtualPath = ((Directory)Parent).VirtualPath + "/" + value
                };
                cmd.Execute(null);

                RaisePropertyChanged("Name");
                RaisePropertyChanged("DisplayName");
            }
        }

        /// <inheritdoc/>
        public IProjectExplorerItem Parent
        {
            get { return projectExplorerHelper.Parent; }
            set { projectExplorerHelper.Parent = value; }
        }

        /// <inheritdoc/>
        public ObservableCollection<IProjectExplorerItem> Children
        {
            get { return projectExplorerHelper.Children; }
        }

        #endregion

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Occurs when the value of some property has changed.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region Event Handlers

        /// <summary>
        /// Occurs when the virtual path to this resource has changed.
        /// </summary>
        /// <param name="sender">Object that has sent the event</param>
        /// <param name="e">Event data</param>
        private void pathElement_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "VirtualPath")
            {
                var path = ((Model.Collections.PathElement)sender).VirtualPath;
                name = path.Substring(path.LastIndexOf('/') + 1);

                RaisePropertyChanged("Name");
                RaisePropertyChanged("DisplayName");
            }
        }

        /// <summary>
        /// Occurs when the value of some property of the <see cref="projectExplorerHelper"/> has changed.
        /// </summary>
        /// <remarks>
        /// <b>Operation:</b><br/>
        /// The handler just resends the event via its own <see cref="ViewModelElement.PropertyChanged"/>.
        /// </remarks>
        /// <param name="sender">The object that has sent the event</param>
        /// <param name="e">Event data</param>
        private void projectExplorerHelper_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            RaisePropertyChanged(e);
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Creates the context menu for this resource.
        /// </summary>
        private void CreateContextMenu()
        {
            var menu = new ContextMenu();
            var mitem = new MenuItem();
            mitem.Icon = EmbeddedBitmapLoader.LoadImage("Pictures.ProjectExplorer.Open.png", 16, 16);
            mitem.Header = (string)LocalizationManager.GetValue("ProjectExplorer.CommandTitles.Open");
            mitem.Command = ProjectExplorerCommands.OpenCommand;
            mitem.CommandParameter = this;
            menu.Items.Add(mitem);

            mitem = new MenuItem();
            mitem.Header = (string)LocalizationManager.GetValue("ProjectExplorer.CommandTitles.Rename");
            mitem.Command = ProjectExplorerCommands.RenameCommand;
            mitem.CommandParameter = this;
            menu.Items.Add(mitem);

            menu.Items.Add(new Separator());

            mitem = new MenuItem();
            mitem.Icon = EmbeddedBitmapLoader.LoadImage("Pictures.ProjectExplorer.Delete.png", 16, 16);
            mitem.Header = (string)LocalizationManager.GetValue("ProjectExplorer.CommandTitles.Delete");
            mitem.Command = ProjectExplorerCommands.DeleteCommand;
            mitem.CommandParameter = this;
            menu.Items.Add(mitem);

            projectExplorerHelper.SetContextMenu(menu);
        }

        #endregion

        #region Protected Methods

        /// <summary>
        /// Raises the <see cref="PropertyChanged"/> event on this <see cref="Resource"/> instance.
        /// </summary>
        /// <param name="propertyName">
        /// <para>
        /// Type: <see cref="System.String"/>
        /// </para>
        /// <para>
        /// The name of the property whose value has changed.
        /// </para>
        /// </param>
        protected void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        /// <summary>
        /// Raises the <see cref="PropertyChanged"/> event on this <see cref="Resource"/> instance.
        /// </summary>
        /// <param name="e">
        /// <para>
        /// Type: <see cref="PropertyChangedEventArgs"/>
        /// </para>
        /// <para>
        /// Event data.
        /// </para>
        /// </param>
        protected void RaisePropertyChanged(PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, e);
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets the resource package part adapted by this resource.
        /// </summary>
        internal PackagePart AdaptedPart
        {
            get { return adaptedPart; }
        }

        /// <summary>
        /// Gets the extension of the resource file.
        /// </summary>
        public string Extension
        {
            get { return extension; }
        }

        /// <summary>
        /// Gets the input stream that can be used to read the data of the resource file.
        /// </summary>
        public System.IO.Stream InputStream
        {
            get { return adaptedPart.GetStream(System.IO.FileMode.Open, System.IO.FileAccess.Read); }
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds a reference to the resource part adapted by this element.
        /// </summary>
        private ResourcePart adaptedPart;
        /// <summary>
        /// Holds the extension of the resource file.
        /// </summary>
        private string extension;
        /// <summary>
        /// Holds the name of this resource.
        /// </summary>
        private string name;
        /// <summary>
        /// Holds a reference to the project that directly owns this resource.
        /// </summary>
        private Project owner;
        /// <summary>
        /// Holds the project explorer helper implementation.
        /// </summary>
        private ProjectExplorerItemHelper projectExplorerHelper;

        #endregion
    }
}
